/***************************************************************************
 *   Copyright (C) 2008 by Ivan Blagoev Topolsky   *
 *   ivan.topolsky@genebio.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#ifndef ANALYSTANALYST_DISPDRIVER_H
#define ANALYSTANALYST_DISPDRIVER_H

#include <iostream>
#include <cstdlib>
#include <winerror.h>

/**
Implements a simple interface to access the couple of Analyst's functions needed to access Wiff files.
For more information about functions themselves see Analyst Cookbook.

This implementation uses "comdriver" a modified version Xiangyang's Dispatch Driver (XYDispDriver),
a driver that entirely uses COM classes only by using the IDispatch base type.

Thus no need *at all* for any form of automatic COM importation in the compiler (like MSVC's #import),
or even having the COM DLL at compile time to begin with.

Instead, the XYDispDriver searches the type library and calls the methods by name at run time (= late binding).

	@author Ivan Blagoev Topolsky <ivan.topolsky@genebio.com>
*/

#include "comutil.h"
#include "comdriver.h"


/**
Class GUID :

Component Object Model (COM) classes (CoClasses) can be accessed either by :
- "CLSID" (class ID) : a GUID  (XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX)
- "ProgID" : a class name ({namespace}.{class}) which can be versioned ({namespace}.{class}.{version})

This can be found using either OLEViewer or RegEdit.
Both CLSID and ProgID for the couple of classes we need are defined below.

When included by other files, DEFINE_GUID will declare the GUIDs as external.
In analyst_dispdriver.cpp they are instanciated
 */

#ifdef ANALYST_INITGUID
#define INITGUID
#include <initguid.h>
#endif

// either use Class GUIDs...
DEFINE_GUID(CLSID_FMANWiffFileControl,	0xA1A48EC4,	0x98C6,	0x11D1,	0xA9, 0xF6, 0x00, 0x60, 0x97, 0x7F, 0x5C, 0x78);
DEFINE_GUID(CLSID_FMANWiffFile ,	0x26e42184,	0x9803,	0x11d1,	0xa9, 0xf6, 0x00, 0x60, 0x97, 0x7f, 0x5c, 0x78);
DEFINE_GUID(CLSID_FMANChromData,	0xC09D0EF6,	0x926D,	0x11D1,	0xA9, 0xF2, 0x00, 0x60, 0x97, 0x7F, 0x5C, 0x78);
DEFINE_GUID(CLSID_FMANSpecData ,	0xC09D0EF8,	0x926D,	0x11D1,	0xA9, 0xF2, 0x00, 0x60, 0x97, 0x7F, 0x5C, 0x78);
DEFINE_GUID(CLSID_Experiment   ,	0x706EAC91,	0x7232,	0x11D1,	0x9D, 0x52, 0x00, 0x60, 0x97, 0x22, 0x30, 0x85);
DEFINE_GUID(CLSID_ExploreProcessing,	0x261B3AB4,	0xB29C,	0x11D1,	0xAA, 0x07, 0x00, 0x60, 0x97, 0x7F, 0x5C, 0x78);
DEFINE_GUID(CLSID_CentroidOptions,	0x45EA0BD3,	0xCE3F,	0x11D1,	0xAA, 0x1F, 0x00, 0x60, 0x97, 0x7F, 0x5C, 0x78);

#define ANALYST_CLSID(CLASS) (CLSID_##CLASS)
// ...or use classe names
#define ANALYST_PRGID(CLASS) "Analyst." #CLASS ".1"


/**
XYDispDriver derivation :
all classes are derivative of Xiangyang's XYDispDriver.
As such they all feature the same kind of declaration, members, etc.
Some macros are defined here to help automate the class creation process
*/

// Macro for often repeated definitions of class & methods
#define BEGINCLASS(CLASS) \
class CLASS##Driver : public XYDispDriver {	\
	public:	\
		inline CLASS##Driver() : XYDispDriver() { \
			if(! CreateObject(ANALYST_CLSID(CLASS))){	\
				cerr << "ERROR:CreateObject " #CLASS << endl;	\
				PrintLastError(cerr);	\
				exit(2);	\
			}	\
		}	\
		inline CLASS##Driver(IDispatch* src) : XYDispDriver(src) { }

// To keep the syntax high lighter happy (because the opening '{' is in the previous macro
#define ENDCLASS };

// Macro to handle Setting a property
#define PUTPROP(CLASS,PROP,TYPE)	\
		const TYPE Put##PROP(const TYPE val) {	\
			if (! SetProperty(# PROP, val, NULL)) {	\
				cerr << "ERROR:Setting " #CLASS "::" #PROP << endl;	\
				PrintLastError(cerr);	\
				exit(2);	\
			}	\
			return val;	\
		}


// Macro to handle getting a property
// ... type BSTR :
#define GETPROP_BSTR(CLASS,PROP)	\
		BSTR Get##PROP() {	\
			VARIANT *v = GetPropertyWithArgs(# PROP, NULL);	\
			if (NULL == v) {	\
				cerr << "ERROR:Getting " #CLASS "::" #PROP << endl;	\
				PrintLastError(cerr);	\
				exit(2);	\
			}	\
			return V_BSTR(v);	\
		}

// ... type BOOL :
#define GETPROP_BOOL(CLASS,PROP)	\
		bool Get##PROP() {	\
			VARIANT *v = GetPropertyWithArgs(# PROP, NULL);	\
			if (NULL == v) {	\
				cerr << "ERROR:Getting " #CLASS "::" #PROP << endl;	\
				PrintLastError(cerr);	\
				exit(2);	\
			}	\
			return V_BOOL(v);	\
		}

// ... type SHORT :
#define GETPROP_SHORT(CLASS,PROP)	\
		short Get##PROP() {	\
			VARIANT *v = GetPropertyWithArgs(# PROP, NULL);	\
			if (NULL == v) {	\
				cerr << "ERROR:Getting " #CLASS "::" #PROP << endl;	\
				PrintLastError(cerr);	\
				exit(2);	\
			}	\
			return V_I2(v);	\
		}

// ... type LONG :
#define GETPROP_LONG(CLASS,PROP)	\
		long Get##PROP() {	\
			VARIANT *v = GetPropertyWithArgs(# PROP, NULL);	\
			if (NULL == v) {	\
				cerr << "ERROR:Getting " #CLASS "::" #PROP << endl;	\
				PrintLastError(cerr);	\
				exit(2);	\
			}	\
			return V_I4(v);	\
		}

// ... type DOUBLE :
#define GETPROP_DOUBLE(CLASS,PROP)	\
		double Get##PROP() {	\
			VARIANT *v = GetPropertyWithArgs(# PROP, NULL);	\
			if (NULL == v) {	\
				cerr << "ERROR:Getting " #CLASS "::" #PROP << endl;	\
				PrintLastError(cerr);	\
				exit(2);	\
			}	\
			return V_R8(v);	\
		}

// Macro to create both property handlers
#define PROP(CLASS,PROP,TYPE)	\
	PUTPROP(CLASS,PROP,TYPE);	\
	GETPROP_##TYPE(CLASS,PROP);

// Does the call and check the result
#define CALLMETHOD(CLASS,METHOD,...)	\
			VARIANT* v = InvokeMethod(#METHOD, __VA_ARGS__, NULL);	\
			if (NULL == v) {	\
				cerr << "ERROR:Calling " #CLASS "::" #METHOD << endl;	\
				PrintLastError(cerr);	\
				wprintf(L"Exception source: %s\n",GetExceptionInfo()->bstrSource);	\
				wprintf(L"Exception description: %s\n",GetExceptionInfo()->bstrDescription);	\
				wprintf(L"Exception help file: %s\n",GetExceptionInfo()->bstrHelpFile);	\
					\
				exit(2);	\
			}


namespace ExploreDataObjects {	// object for manipulation of file and data within are here
	using namespace std;
	
	BEGINCLASS(FMANSpecData)
		// I don't know why, but somewhere along the chain char* are promoted automatically to BSTR. 
		// So no need for conversion
		PUTPROP(FMANSpecData,WiffFileName,char*);
		GETPROP_BSTR(FMANSpecData,WiffFileName);
		GETPROP_BSTR(FMANSpecData,DataTitle);
		GETPROP_SHORT(FMANSpecData,SpectrumType);
		GETPROP_LONG(FMANSpecData,IsCentroided);
		
		int GetNumberOfScans() {
			CALLMETHOD(FMANSpecData,GetNumberOfScans,NULL);
			return V_I4(v);
		};
		
		void SetSpectrum(const long sample, const long period, const long experiment, const float startsec, const float endsec) {
			CALLMETHOD(FMANSpecData,SetSpectrum,sample,period,experiment,startsec,endsec);
			return;
		};
		
		void GetScanInfo(short* scanType, short* scanMode, long* isNegativePolarity, short* q1Resolution, short* q3Resolution) {
			CALLMETHOD(FMANSpecData,GetScanInfo,scanType,scanMode,isNegativePolarity,q1Resolution,q3Resolution);
			return;
		};
		
		int GetNumberOfDataPoints() {
			CALLMETHOD(FMANSpecData,GetNumberOfDataPoints,NULL);
			return V_I4(v);
		};
		
		void GetXValueRange(double *min, double *max) {
			CALLMETHOD(FMANSpecData,GetXValueRange,min,max);
			return;
		};
		
		void GetDataPoint(const long cycle, double *moz, double *intensity) {
			CALLMETHOD(FMANSpecData,GetDataPoint,cycle,moz,intensity);
			return;
		};
		
		double GetDataPointYValue(const long cycle) {
			CALLMETHOD(FMANSpecData,GetDataPointYValue,cycle);
			return V_R8(v);
		};
		
		void GetQ1Mass(const long cycle, double *moz) {
			CALLMETHOD(FMANSpecData,GetQ1Mass,cycle,moz);
			return;
		};
		
		void GetQ3Mass(const long cycle, double *moz) {
			CALLMETHOD(FMANSpecData,GetQ1Mass,cycle,moz);
			return;
		};
		
	ENDCLASS;

	BEGINCLASS(FMANChromData)
		PUTPROP(FMANChromData,WiffFileName,char*);
		GETPROP_BSTR(FMANChromData,WiffFileName);
		GETPROP_BSTR(FMANChromData,DataTitle);

		IUnknown* GetWiffFileObject() {
			CALLMETHOD(FMANChromData,GetWiffFileObject,NULL);
			if (V_VT(v) != VT_UNKNOWN) {
				cerr << "bad return type" << endl;
			}
			V_UNKNOWN(v)->AddRef();
			return V_UNKNOWN(v);
		};
		
		void SetToTIC(const long sample, const long period, const long experiment) {
			CALLMETHOD(FMANChromData,SetToTIC, sample, period, experiment);
			return;
		};

		int GetNumberOfDataPoints() {
			CALLMETHOD(FMANChromData,GetNumberOfDataPoints,NULL);
			return V_I4(v);
		}
		
		void GetXValueRange(double *min, double *max) {
			CALLMETHOD(FMANChromData,GetXValueRange,min,max);
			return;
		}
		
		double GetXValueInSec(const long cycle) {
			CALLMETHOD(FMANChromData,GetXValueInSec,cycle);
			return V_R8(v);
		};

		double GetDataPointYValue(const long cycle) {
			CALLMETHOD(FMANChromData,GetDataPointYValue,cycle);
			return V_R8(v);
		};
	ENDCLASS;
	
	BEGINCLASS(FMANWiffFile)
		double OpenWiffFile(const char* fname) {
			CALLMETHOD(FMANWiffFile,OpenWiffFile,fname);
			return V_R8(v);
		};

		int GetActualNumberOfSamples() {
			CALLMETHOD(FMANWiffFile,GetActualNumberOfSamples,NULL);
			return V_I4(v);
		}

		int GetActualNumberOfPeriods(const long sample) {
			CALLMETHOD(FMANWiffFile,GetActualNumberOfPeriods,sample);
			return V_I4(v);
		}

		BSTR GetSampleName(const long sample) {
			CALLMETHOD(FMANWiffFile,GetSampleName,sample);
			return V_BSTR(v);
		}

		int GetNumberOfExperiments(const long sample, const long period) {
			CALLMETHOD(FMANWiffFile,GetNumberOfExperiments,sample,period);
			return V_I4(v);
		}

		int GetActualNumberOfCycles(const long sample, const long period) {
			CALLMETHOD(FMANWiffFile,GetActualNumberOfCycles,sample,period);
			return V_I4(v);
		}
		
		
		IUnknown* GetExperimentObject(const long sample, const long pe, const long xp) {
			CALLMETHOD(FMANWiffFile,GetExperimentObject,sample,pe,xp);
			V_UNKNOWN(v)->AddRef();
			return V_UNKNOWN(v);
		};
		
		// Not in documentation !!!
		void GetScanInfo(const long sample, const long period, const long experiment, const long cycle,
				 short *scantype, double *fixedmass1, double *fixedmass2, double *collisionenergy, short *chargestate) {
			CALLMETHOD(FMANWiffFile,GetScanInfo, sample, period, experiment, cycle,
				scantype, fixedmass1, fixedmass2, collisionenergy, chargestate);
			return;
		};
	ENDCLASS;

	BEGINCLASS(FMANWiffFileControl)
	ENDCLASS;
}


namespace MSMethodSvrLib {	// parameters of experiment are here
	using namespace std;

	BEGINCLASS(Experiment)
		GETPROP_SHORT(Experiment,ScanType);
		GETPROP_LONG(Experiment,Polarity);
		GETPROP_BSTR(Experiment,Description);
  	ENDCLASS;
}

namespace ExploreDirLib {	// data processing 
	using namespace std;

	// Not in documentation !!!
	BEGINCLASS(CentroidOptions)
		PUTPROP(CentroidOptions,SpectralMergeDistance,double);
		GETPROP_DOUBLE(CentroidOptions,SpectralMergeDistance);
		
		PUTPROP(CentroidOptions,SpectralMinimumWidth,double);
		GETPROP_DOUBLE(CentroidOptions,SpectralMinimumWidth);
		
		PUTPROP(CentroidOptions,SpectralUseMaxForXValue,long);
		GETPROP_LONG(CentroidOptions,SpectralUseMaxForXValue);
		
		void SetToDefault(const long factoryDefault) {
			CALLMETHOD(CentroidOptions,SetToDefault,factoryDefault);
			return;
		};
		
		void DisplayDialog() {
			CALLMETHOD(CentroidOptions,DisplayDialog,NULL);
			return;
		};
		
	ENDCLASS;

	
/**
	BaselineSubtract         Baseline subtracts the provided data object. You also supply a
	‘BaselineSubtractOptions’ object.
	Centroid                 Centroids the provided data object. You also supply a
	‘CentroidOptions’ object.
	GaussianSmooth           Gaussian smooth the data in the data object.
	NoiseFilter              Noise filters the provided data object. You also supply a
	‘NoiseFilterOptions’ object and the minimum required intensity.
	NoiseFilterUsingPeakList Filters the data in the data object to remove noise.
	Smooth                   Smooths the provided data object. You also supply a
	‘SmoothOptions’ object.
	SGSmooth                 Similar to the ‘Smooth’ method, but performs smoothing using a
	Savitzky-Golay algoritm.
	Note that the total number of data points used for the smoothing
	filter is equal to 2*n + 1 where ‘n’ is the second parameter input to
	the method.
 */			


	BEGINCLASS(ExploreProcessing)
			
		void Centroid(IUnknown* options, IUnknown* data) {
			CALLMETHOD(ExploreProcessing,Centroid,options,data);
			return;
		};
		
		void Centroid(CentroidOptionsDriver* options, ExploreDataObjects::FMANSpecDataDriver *data) {
			Centroid(options->GetDispatch(),data->GetDispatch());
			return;
		};
			
	ENDCLASS;
	
	
	
	

}

#endif
